﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Linq;
using System.Xml.Linq;
using System.IO;

#if WINDOWS
using System.CodeDom.Compiler;
using Microsoft.CSharp;
#endif

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Fusion
{
    /// <summary> Secretary handles filing functionality, and is a capable XML translator. </summary>
    public class Secretary
	{
		#region Filename

		/// <summary> Returns the name portion of a path </summary>
        /// <param name="path">The path to search</param>
        /// <returns>Filename string</returns>
        public static string FileName( string path )
        {
            string file = path.Substring( path.LastIndexOf( '/' ) );
            return file.Substring( 0, file.LastIndexOf( '.' ) );
        }

        /// <summary> Returns the file extension portion of a path </summary>
        /// <param name="path">The path to search</param>
        /// <returns>Extension string</returns>
        public static string FileExt( string path )
        {
            string file = path.Substring( path.LastIndexOf( '/' ) );
            return file.Substring( file.LastIndexOf( '.' ) );
        }

        /// <summary> Returns the name.ext portion of a path </summary>
        /// <param name="path">The path to search</param>
        /// <returns>Filename.Extension string</returns>
        public static string FileNameExt( string path )
        { return path.Substring( path.LastIndexOf( '/' ) ); }

		#endregion

        #region String Readers

        public static bool ReadBool( string value )
        {
            if( value == "True" || value == "t" )       return true;
            else										return false;
        }
        public static int ReadInt( string value )
        {
			if( string.IsNullOrEmpty( value ) )
				return 0;
			return int.Parse( value );
		}
        public static float ReadFloat( string value )
		{
			if( string.IsNullOrEmpty( value ) )
				return 0;
			return float.Parse( value );
		}
        public static Rectangle ReadRectangle( string value )
		{
			if( string.IsNullOrEmpty( value ) )
				return new Rectangle(0,0,0,0);
            List<string> entries = SplitCSV( value );
            int x = int.Parse( entries[0] );
            int y = int.Parse( entries[1] );
            int w = int.Parse( entries[2] );
            int h = int.Parse( entries[3] );
            return new Rectangle( x, y, w, h );
        }
        public static Vector2 ReadVector2( string value )
		{
			if( string.IsNullOrEmpty( value ) )
				return Vector2.Zero;
            List<string> entries = SplitCSV( value );
            float x = float.Parse( entries[0] );
            float y = float.Parse( entries[1] );
            return new Vector2( x, y );
        }
        public static Vector3 ReadVector3( string value )
		{
			if( string.IsNullOrEmpty( value ) )
				return Vector3.Zero;
            List<string> entries = SplitCSV( value );
            float x = float.Parse( entries[0] );
            float y = float.Parse( entries[1] );
            float z = float.Parse( entries[2] );
            return new Vector3( x, y, z );
        }
        public static SpriteEffects ReadSpriteEffects( string value )
		{
			if( string.IsNullOrEmpty( value ) )
				return SpriteEffects.None;;
            if( value == "FlipHorizontally" )
                return SpriteEffects.FlipHorizontally;
            else if( value == "FlipVertically" )
                return SpriteEffects.FlipVertically;
            else if( value == "FlipHorizontally, FlipVertically" )
                return ( SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically );
            else
                return SpriteEffects.None;
        }
        public static Color ReadColor( string value )
        {
			if( string.IsNullOrEmpty( value ) )
				return Color.White;
            List<float> list = ReadFloatCSV( value );
            while( list.Count < 4 )
                list.Add( 0 );

            return new Color( list[0], list[1], list[2], list[2] );
        }

        // Comma-Separated Value lists
        public static List<string> SplitCSV( string value )
        {
            List<string> list = new List<string>();

			if( string.IsNullOrEmpty( value ) )
				return list;

            int i = 0;
            while( i >= 0 )
            {
                int next = value.IndexOf( ",", i ) + 1;

                // next is still in the middle of the string; add a new section to the list
                if( next - i > 0 && next >= 0 )
                    list.Add( value.Substring( i, next - i - 1 ) );
                // end of the line!
                else
                {
                    list.Add( value.Substring( i, value.Length - i ) );
                    break;
                }

                i = next;

            }

            return list;
        }
        public static List<bool> ReadBoolCSV( string value )
        {
            List<string> entries = SplitCSV( value );

            List<bool> intList = new List<bool>();
            foreach( string entry in entries )
                intList.Add( ReadBool( entry ) );

            return intList;
        }
        public static List<int> ReadIntCSV( string value )
        {
            List<string> entries = SplitCSV( value );

            List<int> intList = new List<int>();
            foreach( string entry in entries )
                intList.Add( int.Parse( entry ) );

            return intList;
        }
        public static List<float> ReadFloatCSV( string value )
        {
            List<string> entries = SplitCSV( value );

            List<float> floatList = new List<float>();
            foreach( string entry in entries )
                floatList.Add( float.Parse( entry ) );

            return floatList;
        }
        public static List<Vector2> ReadVector2CSV( string value )
        {
            List<string> entries = SplitCSV( value );

            List<Vector2> vectorList = new List<Vector2>();
            for( int i=0; i < entries.Count; i += 2 )
            {
                float x = float.Parse( entries[i] );
                float y = float.Parse( entries[i + 1] );
                vectorList.Add( new Vector2( x, y ) );
            }

            return vectorList;
        }
        public static List<Vector3> ReadVector3CSV( string value )
        {
            List<string> entries = SplitCSV( value );

            List<Vector3> vectorList = new List<Vector3>();
            for( int i=0; i < entries.Count; i += 3 )
            {
                float x = float.Parse( entries[i] );
                float y = float.Parse( entries[i + 1] );
                float z = float.Parse( entries[i + 2] );
                vectorList.Add( new Vector3( x, y, z ) );
            }

            return vectorList;
        }
        #endregion

        #region String Writers

        // Write Rectangle Element
        public static string Write( XElement element, string name, Rectangle value )
		{ return value.X.ToString() + "," + value.Y.ToString() + "," + value.Width.ToString() + "," + value.Height.ToString(); }
        // Write Vector2 element
        public static string Write( Vector2 value )
        { return value.X.ToString() + "," + value.Y.ToString(); }
        // Write Vector3 element
        public static string Write( Vector3 value )
        {
            return value.X.ToString() + "," + value.Y.ToString() + "," + value.Z.ToString();
        }

        // Write Color element
        public static string Write( XElement element, string name, Color value )
        { return value.R.ToString() + "," + value.G.ToString() + "," + value.B.ToString() + "," + value.A.ToString(); }

		/// <summary> Writes a list of int to CSV </summary>
        public static string Write( List<int> list )
        {
            string value = "";
            for( int i=0; i < list.Count; i++ )
            {
                value += list[i];
                if( i < list.Count - 1 )
                    value += ",";
            }

			return value;
        }

        // Write Comma-Separated  float list element
        public static string Write( List<float> list )
        {
            string value = "";
            for( int i=0; i < list.Count; i++ )
            {
                value += list[i];
                if( i < list.Count - 1 )
                    value += ",";
            }

			return value;
        }
        // Write Comma-Separated Vector2 list element
        public static string Write( List<Vector2> list )
        {
            string value = "";
            for( int i=0; i<list.Count; i++ )
            {
                Vector2 vect = list[i];
                value += vect.X.ToString() + "," + vect.Y.ToString();
                if( i < list.Count - 1 )
                    value += ",";
            }

			return value;
        }
        // Write Comma-Separated Vector3 list element
        public static string Write( List<Vector3> list )
        {
            string value = "";
            for( int i=0; i < list.Count; i++ )
            {
                Vector3 vect = list[i];
                value += vect.X.ToString() + "," + vect.Y.ToString() + "," + vect.Z.ToString(); ;
                if( i < list.Count - 1 )
                    value += ",";
            }

			return value;
        }

        #endregion
		
		#region XML

		// XML
        static XDocument doc;
        public static XDocument document
        {
            get
            {
                if( doc == null )
                    NewDocument();
                return doc;
            }
        }
        public static XDocument NewDocument()
        {
			doc = new XDocument( new XDeclaration( "1.0", "", "yes" ), new XElement( "root" ) );
			return doc;
        }

        /// <summary> Loads XML element from disk </summary>
        /// <param name="filename">The complete file path to use</param>
        /// <returns>Loaded XML Element</returns>
        public static XElement Load( string filename )
        {
#if !SILVERLIGHT
            if( File.Exists( filename ) )
#endif
                doc = XDocument.Load( filename );

			if( doc != null )
				return doc.Root;
			else
				return null;
        }

        /// <summary> Finds an element with a specific name inside a particular element.</summary>
        /// <param name="element">The XML element to search</param>
        /// <param name="name">The elementname to search for</param>
        /// <returns>Found XML Element</returns>
        public static XElement FindElement( XElement element, string name )
        {
            foreach( XElement node in element.Descendants() )
                if( node.Name == name )
                    return node;

            return null;
		}

		/// <summary> Adds an XML child element into the target element </summary>
		/// <param name="to">The parent element to add the child to</param>
		/// <param name="name">The child's name</param>
		/// <returns>The newborn child</returns>
		public static XElement AddChild( XElement element, string name )
		{
			XElement child = new XElement( name );
			element.Add( child );
			return child;
		}

		#endregion
		
		#region Code Compilation
		
#if WINDOWS
		/// <summary>
        /// Compiles target .cs file into an object.
        /// Atleast it will, once I get around to making it work properly. No need for runtime compilation just yet though.
        /// </summary>
        /// <param name="filename">The file path to compile</param>
        /// <returns></returns>
        public static T Compile<T>( string file )
        {
            CompilerParameters cParams = new CompilerParameters();
            cParams.ReferencedAssemblies.Add( "system.dll" );
            cParams.ReferencedAssemblies.Add( "system.data.dll" );
            cParams.ReferencedAssemblies.Add( "system.xml.dll" );
            cParams.ReferencedAssemblies.Add( "system.xml.linq.dll" );
            cParams.ReferencedAssemblies.Add( "mscorlib.dll" );
            cParams.GenerateExecutable = false;
            cParams.GenerateInMemory = true;

            var results = new CSharpCodeProvider( new Dictionary<string, string> { { "CompilerVersion", "v3.5" } } ).CompileAssemblyFromFile( cParams, file );

            // Tell the user what went wrong
            if( results.Errors.Count > 0 )
            {
                StringBuilder errors = new StringBuilder();
                for( int i = 0; i < results.Errors.Count; i++ )	//Stupid non-IEnumerables *mutter mutter*
                    errors.AppendLine( results.Errors[i].ErrorText );
                System.Windows.Forms.MessageBox.Show( errors.ToString() );

                return default(T);
            }

            return ( T )results.CompiledAssembly.GetExportedTypes().Where( t => t.IsSubclassOf( typeof( T ) ) ).First().GetConstructor( Type.EmptyTypes ).Invoke( null );

		}
#endif
		#endregion
	}
}
